/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

****/
package org.activequant.core.domainmodel;

import static org.activequant.util.tools.IdentityUtils.equalsTo;
import static org.activequant.util.tools.IdentityUtils.safeCompare;
import static org.activequant.util.tools.IdentityUtils.safeHashCode;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.activequant.core.types.Currency;
import org.activequant.core.types.Exchange;
import org.activequant.core.types.Expiry;
import org.activequant.core.types.SecurityType;


/**
 * Specifies an instrument.
 * <br>
 * <em>IMPORTANT</em>: This object has "value" identity. 
 * It means that its identity
 * (hashCode, equals, and compareTo) is determined by subset of the fields,
 * declared "business keys". Other fields are considered a "payload".
 * This identity reflects the suggested unique key constraints in the
 * related sql table.
 * <em>IMPORTANT</em>: If this object is added to a collection that relies on
 * identity, changing business key will break the collection 
 * contract and will cause unexpected results.
 * <br>
 * Business keys for InstrumentSpecification are:
 * <ul>
 * <li><code>{@link #getSymbol() symbol}</code></li>
 * <li><code>{@link #getExchange() exchange}</code></li>
 * <li><code>{@link #getVendor() vendor}</code></li>
 * <li><code>{@link #getCurrency() currency}</code></li>
 * <li><code>{@link #getSecurityType() securityType}</code></li>
 * <li><code>{@link #getExpiry() expiry}</code></li>
 * <li><code>{@link #getStrike() strike}</code></li>
 * <li><code>{@link #getContractRight() contractRight}</code></li>
 * </ul>
 * <br>
 * Holds the following associated variables:
 * <ul>
 * <li>id(Long)</li>
 * <li>symbol(Symbol)</li>
 * <li>exchange(String)</li>
 * <li>primaryExchange(String)</li>
 * <li>vendor(String)</li>
 * <li>currency(Currency)</li>
 * <li>securityType(SecurityType)</li>
 * <li>expiry(Expiry)</li>
 * <li>lotSize(long)</li>
 * <li>tickSize(double)</li>
 * <li>tickValue(double)</li>
 * <li>strike(double)</li>
 * <li>contractRight(String)</li>
 * <li>decorations(Map&lt;String,String&gt;)</li>
 * </ul>
 * <b>History:</b><br>
 *  - [16.06.2007] Created (Ulrich Staudinger)<br>
 *  - [20.06.2007] Added additional constructor and decorations map (Erik Nijkamp)<br>
 *  - [07.10.2007] Added proper object identity code (hashcode, equals) (Erik Nijkamp)<br>
 *  - [08.10.2007] Made it Comparable, aligned identity with comparable (Mike Kroutikov)<br>
 *  - [10.10.2007] Changing expiry to date. (Ulrich Staudinger)<br>
 *  - [18.10.2007] Fixing to file name method. (Ulrich Staudinger)<br>
 *  - [23.10.2007] Fixing expiry constructors. (Ulrich Staudinger)<br> 
 *  - [24.10.2007] Fixing expiry constructors + fixed equals (Erik Nijkamp)<br>
 *  - [29.10.2007] Minor constructor fixes (Erik Nijkamp)<br> 
 *  - [18.08.2009] Added tick size, lot size, tick value (Ulrich Staudinger)<br>
 *  
 *  @author Ulrich Staudinger
 *  @author Erik Nijkamp
 */
public class InstrumentSpecification implements Comparable<InstrumentSpecification>, Serializable {
	
	private static final long serialVersionUID = -3373322976345588675L;
	/**
	 * public static final int NOT_SET = -1;
	 */
	public static final int NOT_SET = -1;
	/**
	 * private Long id = null;
	 */
	private Long id = null;
	/**
	 * private Symbol symbol = null;
	 */
	private Symbol symbol = null;
	/**
	 * private String exchange = null;
	 */
	private String exchange = null;
	/**
	 * private String primaryExchange = null;
	 */
	private String primaryExchange = null;
	/**
	 * private String vendor = null;
	 */
	private String vendor = null;
	/**
	 * private Currency currency = null;
	 */
	private Currency currency = null;
	/**
	 * private SecurityType securityType = null;
	 */
	private SecurityType securityType = null;
	/**
	 * private Expiry expiry = null;
	 */
	private Expiry expiry = null;
	/**
	 * private long lotSize = NOT_SET;
	 */
	private long lotSize = NOT_SET;
	/**
	 * private double tickSize = NOT_SET;
	 */
	private double tickSize = NOT_SET;
	/**
	 * private double tickValue = NOT_SET;
	 */
	private double tickValue = NOT_SET;

	/**
	 * in case of an option, this is the strike<br/>
	 * private double strike = NOT_SET;
	 */
	private double strike = NOT_SET; 
	/**
	 * in case of an option, this is the right (Call or put or something else)<br/>
	 * private String contractRight = null;
	 */
	private String contractRight = null;
	/**
	 * returns the associated tickSize(double)
	 * @return
	 */
	public double getTickSize() {
		return tickSize;
	}
	/**
	 * sets the associated tickSize(double) with the given one
	 * @param tickSize
	 */
	public void setTickSize(double tickSize) {
		this.tickSize = tickSize;
	}
	/**
	 * can contain decorations.<br/>
	 * private Map&lt;String,String&gt; decorations = new HashMap&lt;String,String&gt;();
	 */
	private Map<String, String> decorations = new HashMap<String, String>(); 
	/**
	 * empty constructor
	 */
	public InstrumentSpecification() {
		
	}
	/**
	 * constructs an InstrumentSpecification using the given symbol(Symbol) to set its associated symbol(Symbol)
	 * @param symbol
	 */
	public InstrumentSpecification(Symbol symbol){
		this.symbol = symbol;
	}

	/**
	 * Constructs an InstrumentSpecification using the given spec(InstrumentSpecification) to copy/set its associated variables.<br/>
	 * copy constructor.
	 * @param instrumentSpecification
	 */
	public InstrumentSpecification(InstrumentSpecification spec) {
		this.id = spec.getId();
		this.symbol = spec.getSymbol();
		this.exchange = spec.getExchange();
		this.vendor = spec.getVendor();
		this.currency = spec.getCurrency();
		this.securityType = spec.getSecurityType();
		this.expiry = spec.getExpiry();
		this.decorations = spec.getDecorationMap();
		this.contractRight = spec.getContractRight();
		this.strike = spec.getStrike();
	}
	
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.<br/>
	 * Using symbol.
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 */
	public InstrumentSpecification(Symbol symbol, String exchange, String vendor,
			Currency currency, SecurityType securityType) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
	}
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.<br/>
	 * Using symbol.
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 */
	public InstrumentSpecification(Symbol symbol, String exchange, String vendor,
			Currency currency, SecurityType securityType, Expiry expiry) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		this.expiry = expiry;
	}
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.<br/>
	 * The given infos(String...) may be an array (even length) of name/value pairs or any number of String parameters (as long as they are even)
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 * @param infos
	 */
	public InstrumentSpecification(Symbol symbol, String exchange, String vendor,
			Currency currency, SecurityType securityType, Expiry expiry, String... infos) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		this.expiry = expiry;
		if(infos != null)
			addDecorations(infos);
	}
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.<br/>
	 * The given infos(String) must be in the <strong>"name1=value1;name2=value2;...."</strong> form, and is used to add those name/value pairs to 
	 * the associated decorations(Map&lt;String,String&gt;)
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 * @param infos
	 */
	public InstrumentSpecification(Symbol symbol, String exchange, String vendor,
			Currency currency, SecurityType securityType, Expiry expiry, String infos) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		this.expiry = expiry;
		addDecorations(infos);
	}

	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 * @param strike
	 * @param contractRight
	 */
	public InstrumentSpecification(Symbol symbol, String exchange,
			String vendor, Currency currency, SecurityType securityType, Expiry expiry,
			double strike, String contractRight) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		if(expiry!=null){
			this.expiry = new Expiry(expiry);
		}
		this.strike = strike;
		this.contractRight = contractRight;
	}
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones.<br/>
	 * The given infos(String) must be in the <strong>"name1=value1;name2=value2;...."</strong> form, and is used to add those name/value pairs to 
	 * the associated decorations(Map&lt;String,String&gt;)
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 * @param strike
	 * @param contractRight
	 * @param infos
	 */
	public InstrumentSpecification(Symbol symbol, String exchange,
			String vendor, Currency currency, SecurityType securityType, Expiry expiry,
			double strike, String contractRight, String infos) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		this.expiry = expiry;
		this.strike = strike;
		this.contractRight = contractRight;
		addDecorations(infos);
	}
	
	/**
	 * Constructs an InstrumentSpecification using the given variables to set its associated ones
	 * @param symbol
	 * @param exchange
	 * @param vendor
	 * @param currency
	 * @param securityType
	 * @param expiry
	 * @param strike
	 * @param contractRight
	 * @param decorations
	 */
	public InstrumentSpecification(Symbol symbol, String exchange,
			String vendor, Currency currency, SecurityType securityType, Expiry expiry,
			double strike, String contractRight, Map<String, String> decorations) {
		this.symbol = symbol;
		this.exchange = exchange;
		this.vendor = vendor;
		this.currency = currency;
		this.securityType = securityType;
		this.expiry = expiry;
		this.strike = strike;
		this.contractRight = contractRight;
		this.decorations = decorations;
	}
	
    /**
     * clone this instance.
     * @return cloned object
     */
    @Override
	public InstrumentSpecification clone() {
		return new InstrumentSpecification(
				symbol,
				exchange,
				vendor,
				currency,
				securityType,
				expiry,
				strike,
				contractRight,
				decorations);
	}
	
    /**
     * return object's state as string.<br/>
     * Returns a string in he form "${id}\t${symbol}\t${exchange}\t${primaryExchange}\t${vendor}\t${currency}\t${securityType}\t${expiry}\t${contractRight}\t${strike}", 
     * where each part is optional (included only if not null)
     * @return text
     */
    @Override
    public String toString() {
		String ret= id==null?"":(id+"\t");
		ret += symbol==null?"":(symbol+"\t");
		ret += exchange==null?"":(exchange+ "\t"); 
		ret += primaryExchange==null?"":(primaryExchange+ "\t"); 
		ret += vendor==null?"":(vendor+ "\t");
		ret += currency==null?"":(currency+ "\t");
		ret +=  securityType==null?"":(securityType + "\t");
		ret += expiry==null?"":(expiry+ "\t");
		ret +=  contractRight==null?"":(contractRight+ "\t");
		ret +=  strike==-1.0?"":(strike);
		return ret;
	}
    
	/**
	 * returns the associated id(Long)
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * sets the associated id(Long) with the given id(Long)
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * returns whether the associated id(Long) is not <code>null</code><br/>
	 * assigned id?
	 * @return
	 */
	public boolean hasId() {
		return id != null;
	}
	/**
	 * returns the associated currency(Currency)
	 * @return
	 */
	public Currency getCurrency() {
		return currency;
	}
	/**
	 * sets the associated currency(Currency) with the given currency(Currency)
	 * @param currency
	 */
	public void setCurrency(Currency currency) {
		this.currency = currency;
	}
	/**
	 * returns the associated exchange(String)
	 * @return
	 */
	public String getExchange() {
		return exchange;
	}
	/**
	 * sets the associated exchange(String) with the given exchange(String)
	 * @param exchange
	 */
	public void setExchange(String exchange) {
		this.exchange = exchange;
	}
	/**
	 * sets the associated exchange(String) with the name(String) (4 character code) of the given exchange(Exchange) enum
	 * @param exchange
	 */
	public void setExchangeEnum(Exchange exchange) {
		this.exchange = exchange.toString();
	}
	/**
	 * sets the associated expiry(Expiry) with the given expiry(Expiry)
	 * @param expiry
	 */
	public void setExpiry(Expiry expiry) {
		this.expiry = expiry;
	}
	/**
	 * returns the associated expiry(Expiry)
	 * @return
	 */
	public Expiry getExpiry() {
		return expiry;
	}
	/**
	 * returns the associated securityType(SecurityType)
	 * @return
	 */
	public SecurityType getSecurityType() {
		return securityType;
	}
	/**
	 * sets the associated securityType(SecurityType) with the given securityType(SecurityType)
	 * @param securityType
	 */
	public void setSecurityType(SecurityType securityType) {
		this.securityType = securityType;
	}
	/**
	 * returns the associated symbol(Symbol)
	 * @return
	 */
	public Symbol getSymbol() {
		return symbol;
	}
	/**
	 * sets the associated symbol(Symbol) with the given symbol(Symbol)
	 * @param symbol
	 */
	public void setSymbol(Symbol symbol) {
		this.symbol = symbol;
	}
	/**
	 * returns the associated vendor(String)
	 * @return
	 */
	public String getVendor() {
		return vendor;
	}
	/**
	 * sets the associated vendor(String) with the given vendor(String)
	 * @param vendor
	 */
	public void setVendor(String vendor) {
		this.vendor = vendor;
	}
	/**
	 * returns whether the associated strike(double) has value other than NOT_SET(-1)
	 * @return
	 */
	public boolean hasStrike() {
		return strike != NOT_SET;
	}
	/**
	 * returns the associated strike(double)
	 * @return
	 */
	public double getStrike() {
		return strike;
	}
	/**
	 * sets the associated strike(double) with the given strike(double)
	 * @param strike
	 */
	public void setStrike(double strike) {
		this.strike = strike;
	}
	/**
	 * returns the associated decorations(Map&lt;String,String&gt;)
	 * @return
	 */
    public Map<String, String> getDecorationMap() {
        return decorations;
    }
    /**
     * sets the associated decorations(Map&lt;String,String&gt;) with the given decorations(Map&lt;String,String&gt;)
     * @param decorations
     */
    public void setDecorationMap(Map<String, String> decorations) {
        this.decorations = decorations;
    }
    /**
     * returns the decoration(String) mapped under the given key(String) in the associated decorations(Map&lt;String,String&gt;)
     * @param key
     * @return
     */
    public String getDecoration(String key) {
    	return decorations.get(key);
    }
    /**
     * returns whether the associated decorations(Map&lt;String,String&gt;) contains the given key(String)
     * @param key
     * @return
     */
    public boolean containsDecoration(String key) {
    	return decorations.containsKey(key);
    }
    /**
     * maps the given key-value pair into the associated decorations(Map&lt;String,String&gt;)
     * @param key
     * @param value
     */
    public void addDecoration(String key, String value) {
    	decorations.put(key, value);
    }
    
	/**
	 * Adds a list of name/value pairs to the map<br/>
	 * Adds the given decorations(String) name/value pairs to the associated decorations(Map&lt;String,String&gt;).<br/>
	 * The given decorations(String) must be in the form: <strong>"name1=value1;name2=value2;...."</strong>
	 * @param decorations name/value pairs separated by a semicolon
	 */
	public void addDecorations(String decorations) {
		String[] split = decorations.split(";");
		for(String pair : split) {
			String[] terms = pair.split("=");
			addDecorations(terms);
		}
	}
	
	/**
	 * Adds a list of name/value pairs to the map<br/>
	 * Adds the given decorations(String[]) name/value pairs to the associated decorations(Map&lt;String,String&gt;).<br/>
	 * The given decorations(String[]) must have an even length (each name must have value)
	 * @param decorations an array of name/value entries
	 */
	public void addDecorations(String[] decorations) {
		assert(decorations.length % 2 == 0);
		for(int i = 0; i < decorations.length; i+=2) {
			addDecoration(decorations[i], decorations[i+1]);
		}
	}
    /**
     * returns whether the associated contractRight(String) is not <code>null</code>
     * @return
     */
	public boolean hasContractRight() {
		return contractRight != null;
	}
	/**
	 * returns the associated contractRight(String)
	 * @return
	 */
	public String getContractRight() {
		return contractRight;
	}
	/**
	 * sets the associated contractRight(String) with the given contractRight(String)
	 * @param contractRight
	 */
	public void setContractRight(String contractRight) {
		this.contractRight = contractRight;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int hashCode() {
		// ATTENTION: keep in sync with compareTo()
		return safeHashCode(this.symbol) 
			+ safeHashCode(this.exchange)
			+ safeHashCode(this.vendor)
			+ safeHashCode(this.currency)
			+ safeHashCode(this.securityType)
			+ safeHashCode(this.expiry)
			+ safeHashCode(this.strike)
			+ safeHashCode(this.contractRight);
	}

	/**
	 * {@inheritDoc}
	 */
	public int compareTo(InstrumentSpecification other) {
		// ATTENTION: keep in sync with hashCode();
		int rc;
		
		rc = safeCompare(this.symbol, other.symbol);
		if(rc != 0) return rc;
		rc = safeCompare(this.exchange, other.exchange);
		if(rc != 0) return rc;
		rc = safeCompare(this.vendor, other.vendor);
		if(rc != 0) return rc;
		rc = safeCompare(this.currency, other.currency);
		if(rc != 0) return rc;
		rc = safeCompare(this.securityType, other.securityType);
		if(rc != 0) return rc;
		rc = safeCompare(this.expiry, other.expiry);
		if(rc != 0) return rc;
		rc = safeCompare(this.strike, other.strike);
		if(rc != 0) return rc;
		rc = safeCompare(this.contractRight, other.contractRight);
		if(rc != 0) return rc;
		
		return rc;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object other) {
		// NOTE: delegates to compareTo()
		return equalsTo(this, other);
	}
	/**
	 * sets the associated lotSize(long) with the given lotSize(long)
	 * @param lotSize
	 */
	public void setLotSize(long lotSize) {
		this.lotSize = lotSize;
	}
	/**
	 * returns the associated lotSize(long)
	 * @return
	 */
	public long getLotSize() {
		return lotSize;
	}
	/**
	 * sets the associated tickValue(double) with the given tickValue(double)
	 * @param tickValue
	 */
	public void setTickValue(double tickValue) {
		this.tickValue = tickValue;
	}
	/**
	 * returns the associated tickValue(double)
	 * @return
	 */
	public double getTickValue() {
		return tickValue;
	}
	/**
	 * returns the associated primaryExchange(String)
	 * @return
	 */
	public String getPrimaryExchange() {
		return primaryExchange;
	}
	/**
	 * sets the associated primaryExchange(String) with the given primaryExchange(String)
	 * @param primaryExchange
	 */
	public void setPrimaryExchange(String primaryExchange) {
		this.primaryExchange = primaryExchange;
	}
	
}